/******************************************************
* USBD_L2_MSDC.c
*
* Purpose: usb controller L2 msdc driver/interface
*
* Author: Eugene Hsu
*
* Date: 2011/10/24
*
* Copyright Generalplus Corp. ALL RIGHTS RESERVED.
*
* Version : 
* History :
*
*******************************************************/
/******************************************************
    Include file
******************************************************/
#include <string.h>
#include "drv_l1_cache.h"
#include "drv_l1_usbd.h"
#include "drv_l2_usbd.h"
#include "drv_l2_usbd_msdc.h"
#include "gplib_mm_gplus.h"

//#define USBDL2MSDCDEG
#ifdef USBDL2MSDCDEG
#define USBD_L2MSDCLOG	print_string
#else
#define USBD_L2MSDCLOG(...)
#endif
extern USBD_MSDC_GP_VENDOR_CTL_BLK	gp_vendor_ctl_blk;
extern INT8U						Default_scsi_inquirydata[];
extern INT8U						Default_scsi_inquirydata_CDROM[];
extern INT32U						RAMDISK_Get_Start_LBA_Address(INT32U lbalen);

/***********************************************/
/******************************************************
    Variables declaration
******************************************************/
const SCSI_SENSE_SET				scsi_sense_tbl[] =
{
	/* Sensekey ASC  ASCQ */
	{ 0x00, 0x00, 0x00, 0 },

	//0x00 - NO SENSE
	{ 0x01, 0x17, 0x01, 0 },

	//0x01 - RECOVERD DATA WITH RETRIES
	{ 0x01, 0x18, 0x00, 0 },

	//0x02 - RECOVERD DATA WITH ECC
	{ 0x02, 0x04, 0x01, 0 },

	//0x03 - LOGICAL DRIVE NOT READY - BECOMING READY
	{ 0x02, 0x04, 0x02, 0 },

	//0x04 - LOGICAL DRIVE NOT READY - INITIALIZATION REQUIRED
	{ 0x02, 0x04, 0x04, 0 },

	//0x05 - LOGICAL UNIT NOT READY - FORMAT IN PROGRESS
	{ 0x02, 0x04, 0xFF, 0 },

	//0x06 - LOGICAL DRIVE NOT READY - DEVICE IS BUSY
	{ 0x02, 0x06, 0x00, 0 },

	//0x07 - NO REFERENCE POSITION FOUND
	{ 0x02, 0x08, 0x00, 0 },

	//0x08 - LOGICAL UNIT COMMUNICATION FAILURE
	{ 0x02, 0x08, 0x01, 0 },

	//0x09 - LOGICAL UNIT COMMUNICATION TIME-OUT
	{ 0x02, 0x08, 0x80, 0 },

	//0x0A - LOGICAL UNIT COMMUNICATION OVERRUN
	{ 0x02, 0x3A, 0x00, 0 },

	//0x0B - MEDIUM NOT PRESENT
	{ 0x02, 0x54, 0x00, 0 },

	//0x0C - USB TO HOST SYSTEM INTERFACE FAILURE
	{ 0x02, 0x80, 0x00, 0 },

	//0x0D - INSUFFICIENT RESOURCES
	{ 0x02, 0xFF, 0xFF, 0 },

	//0x0E - UNKNOWN ERROR
	{ 0x03, 0x02, 0x00, 0 },

	//0x0F - NO SEEK COMPLETE
	{ 0x03, 0x03, 0x00, 0 },

	//0x10 - WRITE FAULT
	{ 0x03, 0x10, 0x00, 0 },

	//0x11 - ID CRC ERROR
	{ 0x03, 0x11, 0x00, 0 },

	//0x12 - UNRECOVERD READ ERROR
	{ 0x03, 0x12, 0x00, 0 },

	//0x13 - ADDRESS MARK NOT FOUND FOR ID FIELD
	{ 0x03, 0x13, 0x00, 0 },

	//0x14 - ADDRESS MARK NOT FOUND FOR DATA FIELD
	{ 0x03, 0x14, 0x00, 0 },

	//0x15 - RECORDED ENTITY NOT FOUND
	{ 0x03, 0x30, 0x01, 0 },

	//0x16 - CANNOT READ MEDIUM - UNKNOWN FORMAT
	{ 0x03, 0x31, 0x01, 0 },

	//0x17 - FORMAT COMMAND FAILED
	{ 0x04, 0x40, 0x80, 0 },

	//0x18 - DIAGNOSTIC FAILURE ON COMPONENT NN (80H-FFH)
	{ 0x05, 0x1A, 0x00, 0 },

	//0x19 - PARAMETER LIST LENGTH ERROR
	{ 0x05, 0x20, 0x00, 0 },

	//0x1A - INVALID COMMAND OPERATION CODE
	{ 0x05, 0x21, 0x00, 0 },

	//0x1B - LOGICAL BLOCK ADDRESS OUT OF RANGE
	{ 0x05, 0x24, 0x00, 0 },

	//0x1C - INVALID FIELD IN COMMAND PACKET
	{ 0x05, 0x25, 0x00, 0 },

	//0x1D - LOGICAL UNIT NOT SUPPORTED
	{ 0x05, 0x26, 0x00, 0 },

	//0x1E - INVALID FIELD IN PARAMETER LIST
	{ 0x05, 0x26, 0x01, 0 },

	//0x1F - PARAMETER NOT SUPPORTED
	{ 0x05, 0x26, 0x02, 0 },

	//0x20 - PARAMETER VALUE INVALID
	{ 0x05, 0x39, 0x00, 0 },

	//0x21 - SAVING PARAMETERS NOT SUPPORT
	{ 0x06, 0x28, 0x00, 0 },

	//0x22 - NOT READY TO READY TRANSITION - MEDIA CHANGED
	{ 0x06, 0x29, 0x00, 0 },

	//0x23 - POWER ON RESET OR BUS DEVICE RESET OCCURRED
	{ 0x06, 0x2F, 0x00, 0 },

	//0x24 - COMMANDS CLEARED BY ANOTHER INITIATOR
	{ 0x07, 0x27, 0x00, 0 },

	//0x25 - WRITE PROTECTED MEDIA
	{ 0x0B, 0x4E, 0x00, 0 },

	//0x26 - OVERLAPPED COMMAND ATTEMPED		
	{ 0xFF, 0xFF, 0xFF, 0 } //0x27 - USB vendor command not have "GP" signature
};

MSDC_LOGIC_UNIT						g_arlun[MAX_MSDC_LUN_NUM] =
{
	/* MAX_MSDC_LUN_NUM = 5 */
	{ 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
	{ 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
	{ 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
	{ 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } },
	{ 0, 0, 0, 0, 0, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }
};

USBD_MSDC_CTL_BLK					msdc_ctlblk;
USBD_MSDC_DMA_BUF					msdc_dma_buf[BOT_DMA_BUF_NUM];	/* for BULK DMA IN/OUT */

INT8U								bulkpiobuf[BOT_TEMP_DATA_LEN];

//static INT8U bulkdmabuf[(BOT_DMA_RAM_BUF_SIZE*BOT_DMA_BUF_NUM) + 16];
static INT8U						*bulkdmabuf;
#define DMA_BULK_IN_MODE	1
#define DMA_BULK_OUT_MODE	2
INT32U								dmabufsize = BOT_DMA_RAM_BUF_SIZE;
INT32U								dmadone = 0;

/******************************************************
    Local utility functions
******************************************************/
void scsi_send_stall_null(void)
{
	/* Check dCBWDataTransferLength, if > 0, send bulk stall(blocking action) */
	if(msdc_ctlblk.cbwdatatcnt)
		drv_l1_usbd_send_bulk_stall_null(msdc_ctlblk.cbw.cbw_cb.bmCBWFlags >> 7);
}

void drv_l2_usbd_msdc_send_csw_data(void)
{
	msdc_ctlblk.dataptr[0] = BOT_CSW_SIGN1;
	msdc_ctlblk.dataptr[1] = BOT_CSW_SIGN2;
	msdc_ctlblk.dataptr[2] = BOT_CSW_SIGN3;
	msdc_ctlblk.dataptr[3] = BOT_CSW_SIGN4;

	/* fill cswtag as dcbwtag */
	msdc_ctlblk.dataptr[4] = msdc_ctlblk.cbw.cbw_cb.dCBWTag[0];
	msdc_ctlblk.dataptr[5] = msdc_ctlblk.cbw.cbw_cb.dCBWTag[1];
	msdc_ctlblk.dataptr[6] = msdc_ctlblk.cbw.cbw_cb.dCBWTag[2];
	msdc_ctlblk.dataptr[7] = msdc_ctlblk.cbw.cbw_cb.dCBWTag[3];

	/* fill Residue */
	msdc_ctlblk.dataptr[8] = msdc_ctlblk.cswdataresidue & 0x000000ff;
	msdc_ctlblk.dataptr[9] = msdc_ctlblk.cswdataresidue & 0x0000ff00;
	msdc_ctlblk.dataptr[10] = msdc_ctlblk.cswdataresidue & 0x00ff0000;
	msdc_ctlblk.dataptr[11] = msdc_ctlblk.cswdataresidue & 0xff000000;

	/* fill command block status */
	msdc_ctlblk.dataptr[12] = msdc_ctlblk.csw.bCSWStatus;

	/* set CSW status */
	msdc_ctlblk.prostate = BULK_CSW_STA;

#if (USBD_BULKEP == BULK_EP12)
	drv_l1_usbd_send_bulk_in(msdc_ctlblk.dataptr, BOT_CSW_LEN);
#elif (USBD_BULKEP == BULK_EP89)
	drv_l1_usbd_send_bulk89_in(msdc_ctlblk.dataptr, BOT_CSW_LEN);
#endif
	USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_msdc_send_csw_data()!!\r\n");
}

/**************************************************************
     		  bit  +---+---+---+---+---+---+---+---+
         Byte      | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
                   +---+---+---+---+---+---+---+---+
           0-3     |        dCBWSignature          |
                   +-------------------------------+
           4-7     |           dCBWTag             |
                   +-------------------------------+
           8-11    |     dCBWDataTransferLength    |
                   +---------------+---------------+
            12     |        bmCBWFlags             |
                   +---------------+---------------+
            13     |  Reserved(0)  | bCBWLUN       |
                   +-----------+---+---------------+
            14     |Reserved(0)|  bCBWCBLength     |
                   +-------------------------------+
           15-30   |             CBWCB             |
                   +-------------------------------+
***************************************************************/
INT32S drv_l2_usbd_msdc_get_cbw_data(void)
{
	INT32U	len = BOT_CBW_LEN /*, i*/ ;

	//INT32U i;
#if (USBD_BULKEP == BULK_EP12)
	/* Get CBW length */
	len = drv_l1_usbd_get_bulk_out_cnt();

	/* get total cbw raw pkt */
	drv_l1_usbd_rec_bulk_out((void *) msdc_ctlblk.dataptr, len);
#elif (USBD_BULKEP == BULK_EP89)
	len = drv_l1_usbd_get_bulk89_out_cnt();
	drv_l1_usbd_get_bulk89_out_pkt_data((void *) msdc_ctlblk.dataptr, len);
#endif
	memcpy((INT8S *) msdc_ctlblk.cbw.cbw_raw, (INT8S *) msdc_ctlblk.dataptr, len);

	if
	(
		(len != BOT_CBW_LEN)
	||	(
			msdc_ctlblk.cbw.cbw_cb.dCBWSignature[0] != BOT_CBW_SIGN1
		||	msdc_ctlblk.cbw.cbw_cb.dCBWSignature[1] != BOT_CBW_SIGN2
		||	msdc_ctlblk.cbw.cbw_cb.dCBWSignature[2] != BOT_CBW_SIGN3
		||	msdc_ctlblk.cbw.cbw_cb.dCBWSignature[3] != BOT_CBW_SIGN4
		)
	||	(msdc_ctlblk.cbw.cbw_cb.bCBWLUN > drv_l2_usbd_msdc_get_max_lun())
	||	(msdc_ctlblk.cbw.cbw_cb.bCBWCBLength > 0x10 || msdc_ctlblk.cbw.cbw_cb.bCBWCBLength == 0)
	)
	{
		/* Set Bulk IN/OUT stall */
#if (USBD_BULKEP == BULK_EP12)
		drv_l1_usbd_set_ep_stall(MASK_USBD_UDLC_STL_SETEP1STL | MASK_USBD_UDLC_STL_SETEP2STL, 0);
#elif (USBD_BULKEP == BULK_EP89)
		drv_l1_usbd_set_ep_stall(MASK_USBD_UDLC_STL_SETEP8STL | MASK_USBD_UDLC_STL_SETEP9STL, 0);
#endif
		msdc_ctlblk.prostate = BULK_CBW_STA;

		//DBG_PRINT("drv_l2_usbd_msdc_get_cbw_data error, len = %d, != 31\r\n", len);
		DBG_PRINT("drv_l2_usbd_msdc_get_cbw_data error, len = %d\r\n", len);
#if 0
		DBG_PRINT
		(
			"[0x%x][0x%x][0x%x][0x%x]\r\n",
			msdc_ctlblk.cbw.cbw_cb.dCBWSignature[0],
			msdc_ctlblk.cbw.cbw_cb.dCBWSignature[1],
			msdc_ctlblk.cbw.cbw_cb.dCBWSignature[2],
			msdc_ctlblk.cbw.cbw_cb.dCBWSignature[3]
		);
		DBG_PRINT("CBWLUN[0x%x], lun = 0x%x\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN, drv_l2_usbd_msdc_get_max_lun());
		DBG_PRINT("bCBWCBLength[0x%x]\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWCBLength);
#endif
#if 0
		for(i = 0; i < 30; i++)
		{
			DBG_PRINT
			(
				"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\r\n",
				msdc_ctlblk.cbw.cbw_raw[i],
				msdc_ctlblk.cbw.cbw_raw[i + 1],
				msdc_ctlblk.cbw.cbw_raw[i + 2],
				msdc_ctlblk.cbw.cbw_raw[i + 3],
				msdc_ctlblk.cbw.cbw_raw[i + 4],
				msdc_ctlblk.cbw.cbw_raw[i + 5]
			);
			i += 6;
		}

		DBG_PRINT("0x%x\r\n", msdc_ctlblk.cbw.cbw_raw[30]);
#endif
		return USBD_RET_ERROR;
	}

	msdc_ctlblk.cbwdatatcnt = msdc_ctlblk.cbw.cbw_cb.dCBWDataTransferLength[0] + (msdc_ctlblk.cbw.cbw_cb.dCBWDataTransferLength[1] << 8) + (msdc_ctlblk.cbw.cbw_cb.dCBWDataTransferLength[2] << 16) + (msdc_ctlblk.cbw.cbw_cb.dCBWDataTransferLength[3] << 24);

	msdc_ctlblk.prostate = BULK_CBW_STA;

	USBD_L2MSDCLOG
	(
		"USBDL2MSDC: _get_cbw_pkt, len %d, bCBWLUN %d, bCDWCBLength %d, bmCBWFlags %d!!\r\n",
		msdc_ctlblk.cbwdatatcnt,
		msdc_ctlblk.cbw.cbw_cb.bCBWLUN,
		msdc_ctlblk.cbw.cbw_cb.bCBWCBLength,
		msdc_ctlblk.cbw.cbw_cb.bmCBWFlags
	);
	return USBD_RET_SUCCESS;
}

/**************************************************************
    Funciton: scsi_command_fail()
    Purpose: Return command fail when SCSI command error
             1.Update lun.senseidx
**************************************************************/
void scsi_command_fail(INT32U senseidx)
{
	/* store sense code, according to SCSISENSE table */
	msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN].senseidx = senseidx;

	msdc_ctlblk.prostate = BULK_CSW_STA;				/* Enter CSW state, and state is failed */
	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_FAILED_STA;	// SCSI command failed
	msdc_ctlblk.csw.dCSWDataResidue = msdc_ctlblk.cbwdatatcnt;

	USBD_L2MSDCLOG("USBDL2MSDC: scsi_command_fail, lun %d, senseidx 0x%x \r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN, senseidx);
	drv_l2_usbd_msdc_send_csw_data();
}

/**************************************************************
    Funciton: drv_l2_usbd_msdc_check_lun_status()
    Purpose: check lun status, including
             1. lun number vs max lun.
             2. media connection             
**************************************************************/
INT8S drv_l2_usbd_msdc_check_lun_status(void)
{
	MSDC_LOGIC_UNIT *lun_ptr;

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	/* check bCBWLUN is legal or not */
	if(msdc_ctlblk.cbw.cbw_cb.bCBWLUN > msdc_ctlblk.maxlun)
	{
		msdc_ctlblk.prostate = BULK_CBW_STA;

		/* Bulk IN/OUT endpoint enter STALL condition, to do */
		scsi_send_stall_null();
		USBD_L2MSDCLOG("USBDL2MSDC: lun %d, CBS LUN %d, maxlun %d \r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN, msdc_ctlblk.maxlun);
		return USBD_RET_ERROR;
	}

	/* Detect current lun storage type */
	if((lun_ptr->status == LUNSTS_NOMEDIA) || (lun_ptr->status == LUNSTS_START_STOP_NOMEDIA))
	{
		USBD_L2MSDCLOG("USBDL2MSDC: lun %d, lun_ptr->status LUNSTS_NOMEDIA \r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		scsi_send_stall_null();
		scsi_command_fail(0x0b);				/* NoMedia */
		return USBD_RET_ERROR;
	}
	else
	if((lun_ptr->status & LUNSTS_MEDIACHANGE))
	{
		USBD_L2MSDCLOG("Check LUN %d failed, media changed..\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		scsi_send_stall_null();
		scsi_command_fail(0x22);				/* Media change */
		lun_ptr->status ^= LUNSTS_MEDIACHANGE;	/* Remove media change status */
		return USBD_RET_ERROR;
	}

	return USBD_RET_SUCCESS;
}

void drv_l2_usbd_msdc_get_read_lba_len(INT32U *lba, INT32U *len, INT8U type)
{
	if(type == 10)
	{
		*lba = msdc_ctlblk.cbw.cbw_cb.CBWCB[2] << 24;
		*lba += msdc_ctlblk.cbw.cbw_cb.CBWCB[3] << 16;
		*lba += msdc_ctlblk.cbw.cbw_cb.CBWCB[4] << 8;
		*lba += msdc_ctlblk.cbw.cbw_cb.CBWCB[5];

		*len = msdc_ctlblk.cbw.cbw_cb.CBWCB[7] << 8;
		*len += msdc_ctlblk.cbw.cbw_cb.CBWCB[8];
	}
	else
	if(type == 12)
	{
		*lba = msdc_ctlblk.cbw.cbw_cb.CBWCB[2] << 24;
		*lba += msdc_ctlblk.cbw.cbw_cb.CBWCB[3] << 16;
		*lba += msdc_ctlblk.cbw.cbw_cb.CBWCB[4] << 8;
		*lba += msdc_ctlblk.cbw.cbw_cb.CBWCB[5];

		*len = msdc_ctlblk.cbw.cbw_cb.CBWCB[6] << 24;
		*len += msdc_ctlblk.cbw.cbw_cb.CBWCB[7] << 16;
		*len += msdc_ctlblk.cbw.cbw_cb.CBWCB[8] << 8;
		*len += msdc_ctlblk.cbw.cbw_cb.CBWCB[9];
	}
}

#if (_OPERATING_SYSTEM != _OS_NONE)
void drv_l2_usbd_msdc_do_read_scsi_cmd(void)
{
	INT32S			ret = -1;
	MSDC_LOGIC_UNIT *lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	if(!msdc_ctlblk.nodmadata)
	{
		//DBG_PRINT("USBDL2MSDC: drv_l2_usbd_msdc_do_read_scsi_cmd before bulk in\r\n");
		/* send the data in RAM buffer to USB */
#if (USBD_BULKEP == BULK_EP12)
		drv_l1_usbd_send_bulk_in((INT32U *) msdc_ctlblk.dmaptr->dataptr, msdc_ctlblk.dmaptr->usedbuffsize);
#elif (USBD_BULKEP == BULK_EP89)
		drv_l1_usbd_send_bulk89_in((INT32U *) msdc_ctlblk.dmaptr->dataptr, msdc_ctlblk.dmaptr->usedbuffsize);
#endif

		//DBG_PRINT("USBDL2MSDC: drv_l2_usbd_msdc_do_read_scsi_cmd after bulk in\r\n");
		/* Change to the other RAM buffer */
		msdc_ctlblk.dmaptr = msdc_ctlblk.dmaptr->next;

		if(msdc_ctlblk.rambufcnt)
		{
			if(lun_ptr->storageaccess.usbd_msdc_read_dma_phase != NULL)
			{
				ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->dataptr, 1, (dmabufsize / MSDC_BLOCK_SIZE));
#if (_DRV_L1_CACHE == 1)
				cache_drain_range((INT32U) msdc_ctlblk.dmaptr->dataptr, (INT32U) dmabufsize);
#endif
				if(ret)
					return;
			}

			msdc_ctlblk.dmaptr->usedbuffsize = dmabufsize;
			msdc_ctlblk.rambufcnt--;
		}
		else
		if(msdc_ctlblk.rambufres)
		{
			if(lun_ptr->storageaccess.usbd_msdc_read_dma_phase != NULL)
			{
				ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->dataptr, 1, msdc_ctlblk.rambufres);
#if (_DRV_L1_CACHE == 1)
				cache_drain_range((INT32U) msdc_ctlblk.dmaptr->dataptr, (INT32U) msdc_ctlblk.rambufres);
#endif
				if(ret)
					return;
			}

			msdc_ctlblk.dmaptr->usedbuffsize = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
			msdc_ctlblk.rambufres = 0;
		}
		else
		{
			/* no DMA data to send */
			msdc_ctlblk.nodmadata = 1;
			if(lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase != NULL)
			{
				lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase(lun_ptr->storageaccess.priv);
			}
		}
	}
	else
	{
		msdc_ctlblk.cswdataresidue = 0;
		msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;

		/* send CSW */
		drv_l2_usbd_msdc_send_csw_data();
	}
}

#endif

/******************************************************
    Utility function declaration
******************************************************/

/******************************************************
    State function declaration
    	
    	drv_l2_usbd_msdc_state_bot_in()
    	drv_l2_usbd_msdc_state_bot_out()
******************************************************/
void drv_l2_usbd_msdc_state_bot_in(INT32U event)
{
	switch(event)
	{
	case USBD_BULK_IN_ACK_EVENT:
	case USBD_BULK_89_IN_ACK_EVENT:
#if (USBD_BULKEP == BULK_EP12)
		/* disable bulk in/out pkt 0x330[1] */
		drv_l1_usbd_disable_bulk_in_out();

		//DBG_PRINT("drv_l2_usbd_msdc_state_bot_in , event %d, msdc_ctlblk.prostate = %d\r\n", event, msdc_ctlblk.prostate);
#elif (USBD_BULKEP == BULK_EP89)
		drv_l1_usbd_disable_bulk89_in_out();
#endif
		if(msdc_ctlblk.prostate == BULK_DATA_STA)
		{
			INT8U	dir = msdc_ctlblk.cbw.cbw_cb.bmCBWFlags >> 7;
			if(dir == CBW_DATA_DIR_IN)
			{
				if(msdc_ctlblk.cbwdatatcnt == 0)
				{
					/* send csw pkt*/
					drv_l2_usbd_msdc_send_csw_data();
				}
				else
				{
					/* msdc_ctlblk.cbwdatatcnt > 0, stall bulk IN pipe, for USB 2.0 test suite */
					DBG_PRINT("IN ACK, data cnt = 0x%x, stall IN pipe\r\n", msdc_ctlblk.cbwdatatcnt);
					drv_l1_usbd_send_bulk_stall_null(CBW_DATA_DIR_IN);
					msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
					msdc_ctlblk.cswdataresidue = 0;
					msdc_ctlblk.cbwdatatcnt = 0;
					drv_l2_usbd_msdc_send_csw_data();
				}
			}
		}
		else
		if(msdc_ctlblk.prostate == BULK_CSW_STA)
		{
			//USBD_L2MSDCLOG("USBDL2MSDC: usbd_l2_msdc_bot_in() BULK_CSW_STA!!\r\n");
			//DBG_PRINT("usbd_l2_msdc_bot_in() BULK_CSW_STA, opcode = 0x%x!!\r\n", msdc_ctlblk.cbw.cbw_cb.CBWCB[0]);
			/* enable bulk out pkt to receive next CBW and reset BOT state to CBW */
			msdc_ctlblk.prostate = BULK_CBW_STA;
#if (USBD_BULKEP == BULK_EP12)
			drv_l1_usbd_enable_rec_bulk_out_pkt();
#elif (USBD_BULKEP == BULK_EP89)
			drv_l1_usbd_enable_rec_bulk89_out_pkt();
#endif
		}

		break;

	case USBD_BULK_IN_DMA_TRANS_DONE_EVENT:
	case USBD_BULK_89_IN_DMA_TRANS_DONE_EVENT:
#if (USBD_BULKEP == BULK_EP12)
		/* disable bulk in/out pkt 0x330[1] */
		drv_l1_usbd_disable_bulk_in_out();

		//DBG_PRINT("drv_l2_usbd_msdc_state_bot_in , event %d, msdc_ctlblk.prostate = %d\r\n", event, msdc_ctlblk.prostate);
#elif (USBD_BULKEP == BULK_EP89)
		drv_l1_usbd_disable_bulk89_in_out();
#endif
		dmadone = DMA_BULK_IN_MODE;

		if(msdc_ctlblk.prostate == BULK_DATA_STA)
		{
#if (_OPERATING_SYSTEM == _OS_NONE)
			dmadone = DMA_BULK_IN_MODE;

#else
			drv_l2_usbd_msdc_do_read_scsi_cmd();
#endif
		}

		break;

	case USBD_BULK_IN_NACK_EVENT:
		DBG_PRINT("EP1 IN NAK\r\n");
		rDLCIE_UDLC |= MASK_USBD_UDLC_IE_EP1N;
		break;

	case USBD_BULK_89_IN_NACK_EVENT:
		DBG_PRINT("EP8 IN NAK\r\n");
		rNEWEP_IE |= MASK_USBD_NEWEP_IE_EP8N;
		break;

	default:
		break;
	}
}

void drv_l2_usbd_msdc_state_bot_out(INT32U event)
{
	switch(event)
	{
	case USBD_BULK_OUT_READY_EVENT:
	case USBD_BULK_89_OUT_READY_EVENT:
#if (USBD_BULKEP == BULK_EP12)
		/* diable bulk in/out 0x330[1] */
		drv_l1_usbd_disable_bulk_in_out();

		//DBG_PRINT("drv_l2_usbd_msdc_state_bot_out event %d\r\n", event);
#elif (USBD_BULKEP == BULK_EP89)
		drv_l1_usbd_disable_bulk89_in_out();
#endif
		if(msdc_ctlblk.prostate == BULK_CBW_STA)
		{
			//drv_l1_usbd_switch_fifo_to_valid_data();
			if(drv_l2_usbd_msdc_get_cbw_data() == USBD_RET_SUCCESS)
			{
				/* process SCSI command */
				drv_l2_usbd_scsi_command_process();
			}
			else
			{
				//DBG_PRINT("Get CBW data error, stall BULK IN/OUT...\r\n");
			}
		}
		else
		if(msdc_ctlblk.prostate == BULK_DATA_STA)
		{
			/* continuous to receive data from host */
			//DBG_PRINT("IN USBD_BULK_OUT_READY_EVENT\r\n");
		}

		break;

	case USBD_BULK_OUT_M_DMA_DONE_EVENT:
	case USBD_BULK_OUT_89_M_DMA_DONE_EVENT:
#if (USBD_BULKEP == BULK_EP12)
		/* diable bulk in/out 0x330[1] */
		drv_l1_usbd_disable_bulk_in_out();

		//DBG_PRINT("drv_l2_usbd_msdc_state_bot_out event %d\r\n", event);
#elif (USBD_BULKEP == BULK_EP89)
		drv_l1_usbd_disable_bulk89_in_out();
#endif
		drv_l1_usbd_reset_bulk_dma_type();

		if(msdc_ctlblk.prostate == BULK_DATA_STA)
		{
#if (_OPERATING_SYSTEM == _OS_NONE)
			dmadone = DMA_BULK_OUT_MODE;
#else
			MSDC_LOGIC_UNIT *lun_ptr;
			INT32U			*toscsiptr;
			INT32U			presecnum, transfer_len;
			INT32S			ret;

			lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

			toscsiptr = (INT32U *) msdc_ctlblk.dmaptr->dataptr;
			presecnum = msdc_ctlblk.secnum;
			msdc_ctlblk.dmaptr = msdc_ctlblk.dmaptr->next;
			if(msdc_ctlblk.rambufcnt)
			{
				msdc_ctlblk.secnum = (dmabufsize / MSDC_BLOCK_SIZE);
				transfer_len = dmabufsize;	/* Full RAM buffer size */
				msdc_ctlblk.rambufcnt--;
			}
			else
			if(msdc_ctlblk.rambufres)
			{
				msdc_ctlblk.secnum = msdc_ctlblk.rambufres;
				transfer_len = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
				msdc_ctlblk.rambufres = 0;
			}
			else
			{
				msdc_ctlblk.nodmadata = 1;
				msdc_ctlblk.secnum = 0;
				transfer_len = 0;
			}

			if(!msdc_ctlblk.nodmadata)
			{
#if (USBD_BULKEP == BULK_EP12)
				drv_l1_usbd_rec_bulk_out((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len);
#elif (USBD_BULKEP == BULK_EP89)
				drv_l1_usbd_get_dma_bulk89_out_data_by_len((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len);
#endif
				if(lun_ptr->storageaccess.usbd_msdc_write_dma_phase != NULL)
				{
#if (_DRV_L1_CACHE == 1)
					cache_invalid_range((INT32U) toscsiptr, (presecnum << 9));
#endif
					ret = lun_ptr->storageaccess.usbd_msdc_write_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) toscsiptr, 1, presecnum);
					if(ret)
						return;
				}
			}
			else
			{
				if(lun_ptr->storageaccess.usbd_msdc_write_dma_phase != NULL)
				{
#if (_DRV_L1_CACHE == 1)
					cache_invalid_range((INT32U) toscsiptr, (presecnum << 9));
#endif
					ret = lun_ptr->storageaccess.usbd_msdc_write_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) toscsiptr, 1, presecnum);
					if(ret)
						return;
				}

				/* Receive done, enter CSW stage */
				if(lun_ptr->storageaccess.usbd_msdc_write_cmdend_phase != NULL)
				{
					lun_ptr->storageaccess.usbd_msdc_write_cmdend_phase(lun_ptr->storageaccess.priv);
				}

				msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
				msdc_ctlblk.csw.dCSWDataResidue = 0;
				drv_l2_usbd_msdc_send_csw_data();
			}

#endif
		}

		break;

	case USBD_BULK_OUT_NACK_EVENT:
		DBG_PRINT("EP2 OUT NAK\r\n");
		rDLCIE_UDLC |= MASK_USBD_UDLC_IE_EP2N;
		break;

	case USBD_BULK_89_OUT_NACK_EVENT:
		DBG_PRINT("EP9 OUT NAK\r\n");
		rNEWEP_IE |= MASK_USBD_NEWEP_IE_EP9N;
		break;

	default:
		break;
	}
}

void drv_l2_usbd_scsi_command_process(void)
{
	INT8U	opcode = msdc_ctlblk.cbw.cbw_cb.CBWCB[0];

	/* Parse SCSI command from CBWCB */
	USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_scsi_command_process opcode 0x%x, dir %d!!\r\n", opcode, dir);

	//DBG_PRINT("USBDL2MSDC: drv_l2_usbd_scsi_command_process opcode 0x%x!!\r\n", opcode);
	/* Check if SCSI vendor command */
	if(opcode >= 0xF0)
	{
		/* Run SCSI vendor command */
		if(msdc_ctlblk.process_scsi_vendor_cmd != NULL)
		{
			INT16U	cmdval = (msdc_ctlblk.cbw.cbw_cb.CBWCB[0] << 8) | (msdc_ctlblk.cbw.cbw_cb.CBWCB[1]);
			msdc_ctlblk.process_scsi_vendor_cmd(cmdval);
		}
		else
		{
			//DBG_PRINT("Prcess SCSI vendor commadn but handle is NULL.\r\n");
			scsi_send_stall_null();
			scsi_command_fail(0x1A);		//command not support
		}

		return;
	}

	/* Run statndard SCSI command */
	switch(opcode)
	{
	case SCSI_INQUIRY_OPCODE:
		drv_l2_usbd_scsi_cmd_inquiry();
		break;

	case SCSI_MODESENSE6_OPCODE:
		drv_l2_usbd_scsi_cmd_modesense6();
		break;

	case SCSI_READFORMATCAPACITY_OPCODE:
		drv_l2_usbd_scsi_cmd_formatcapacity();
		break;

	case SCSI_READCAPACITY_OPCODE:
		drv_l2_usbd_scsi_cmd_readcapacity();
		break;

	case SCSI_READ10_OPCODE:
		drv_l2_usbd_scsi_cmd_read10();
		break;

	case SCSI_TESTUNITREADY_OPCODE:
		drv_l2_usbd_scsi_cmd_testunitready();
		break;

	case SCSI_WRITE10_OPCODE:
		drv_l2_usbd_scsi_cmd_write10();
		break;

	case SCSI_MEDIUMREMOVAL_OPCODE:
		drv_l2_usbd_scsi_cmd_mediumremoval();
		break;

	case SCSI_REQUSETSENSE_OPCODE:
		drv_l2_usbd_scsi_cmd_requestsense();
		break;

	case SCSI_VERIFY_OPCODE:
		drv_l2_usbd_scsi_cmd_verify();
		break;

	case SCSI_STARTSTOP_OPCODE:
		drv_l2_usbd_scsi_cmd_startstop();
		break;

	/* To do, Not implement yet */
	case SCSI_READTOC_OPCODE:
	case SCSI_MODESENSE10_OPCODE:
	default:
		scsi_send_stall_null();
		scsi_command_fail(0x1A);			//command not support
		break;
	}

	//DBG_PRINT("opcode 0x%x, data cnt = 0x%x\r\n", opcode, msdc_ctlblk.cbwdatatcnt);
}

/******************************************************
    SCSI cmd function declaration

    drv_l2_usbd_scsi_cmd_modesense6()
    drv_l2_usbd_scsi_cmd_testunitready()
    drv_l2_usbd_scsi_cmd_readcapacity()
    drv_l2_usbd_scsi_cmd_read10()
    drv_l2_usbd_scsi_cmd_write10()
    drv_l2_usbd_scsi_cmd_mediumremoval()
    drv_l2_usbd_scsi_cmd_requestsense()
    drv_l2_usbd_scsi_cmd_formatcapacity()
    drv_l2_usbd_scsi_cmd_inquiry()
    drv_l2_usbd_scsi_cmd_verify()
******************************************************/
void drv_l2_usbd_scsi_cmd_modesense6(void)
{
	//str_USB_Lun_Info* lun_ptr;
	MSDC_LOGIC_UNIT *lun_ptr;
	INT32U			sense6;

	if(drv_l2_usbd_msdc_check_lun_status() == USBD_RET_ERROR)
	{
		USBD_L2MSDCLOG("drv_l2_usbd_scsi_cmd_modesense6 check lun fail lun %d\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		return;
	}

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	if(lun_ptr->wprotect)
	{
		sense6 = 0x80;
	}
	else
	{
		sense6 = 0x00;
	}

	msdc_ctlblk.dataptr[0] = 0x03;
	msdc_ctlblk.dataptr[1] = 0x00;
	msdc_ctlblk.dataptr[2] = sense6;
	msdc_ctlblk.dataptr[3] = 0x00;

	msdc_ctlblk.prostate = BULK_DATA_STA;
#if (USBD_BULKEP == BULK_EP12)
	drv_l1_usbd_send_bulk_in(msdc_ctlblk.dataptr, 4);
#elif (USBD_BULKEP == BULK_EP89)
	drv_l1_usbd_send_bulk89_in(msdc_ctlblk.dataptr, 4);
#endif
	msdc_ctlblk.cbwdatatcnt = 0;
	msdc_ctlblk.cswdataresidue = 0;
	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
}

/***********************************************************************
*   void drv_l2_usbd_scsi_cmd_testunitready(void)                      *
*   opcode: 0x00(0)                                                    *
*   purpose: Return the status that the logical unit is ready.         *  
*                                                                      *
*                                                                      *  
***********************************************************************/
void drv_l2_usbd_scsi_cmd_testunitready(void)
{
	//MSDC_LOGIC_UNIT* lun_ptr;
	//INT8U lunnum = msdc_ctlblk.cbw.cbw_cb.bCBWLUN;
	if(drv_l2_usbd_msdc_check_lun_status() == USBD_RET_ERROR)
	{
		USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_scsi_cmd_testunitready check lun fail, lun %d\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		return;
	}

	/* If flag = OUT > 0, stall the pipe and return CSW with command failed. For USB 2.0 test suite */
	if((msdc_ctlblk.cbw.cbw_cb.bmCBWFlags >> 7) == CBW_DATA_DIR_OUT)
	{
		if((msdc_ctlblk.cbwdatatcnt > 0))
		{
			/* Ho > Dn */
			drv_l1_usbd_send_bulk_stall_null(CBW_DATA_DIR_OUT);

			/* Send CSW command back to host */
			msdc_ctlblk.csw.bCSWStatus = CSW_CMD_FAILED_STA;
			msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
			drv_l2_usbd_msdc_send_csw_data();
			return;
		}
	}

	/* So far, return "good status" directly */
	msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;

	/* no data stage, reply CSW */
	drv_l2_usbd_msdc_send_csw_data();
}

/***********************************************************************
*   void drv_l2_usbd_scsi_cmd_readcapacity(void)                       *
*   opcode: 0x25(37)                                                   *
*   purpose: Return the total capacities of the currently installed    *
*            medium.                                                   *  
*                                                                      *
*                                                                      *  
***********************************************************************/
void drv_l2_usbd_scsi_cmd_readcapacity(void)
{
	MSDC_LOGIC_UNIT *lun_ptr;

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	if(drv_l2_usbd_msdc_check_lun_status() == USBD_RET_ERROR)
	{
		USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_scsi_cmd_readcapacity check lun fail, lun %d\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		return;
	}

	USBD_L2MSDCLOG("USBDL2MSDC: Read capacity, lun_ptr->secSize 0x%2x\r\n", lun_ptr->secSize);

	/* Total sector number */
	if(lun_ptr->secSize == 0)
	{
		msdc_ctlblk.dataptr[0] = 0;
		msdc_ctlblk.dataptr[1] = 0;
		msdc_ctlblk.dataptr[2] = 0;
		msdc_ctlblk.dataptr[3] = 0;
	}
	else
	{
		msdc_ctlblk.dataptr[0] = (lun_ptr->secSize - 1) >> 24;
		msdc_ctlblk.dataptr[1] = (lun_ptr->secSize - 1) >> 16;
		msdc_ctlblk.dataptr[2] = (lun_ptr->secSize - 1) >> 8;
		msdc_ctlblk.dataptr[3] = (lun_ptr->secSize - 1);
	}

	/* Sector Size (Bytes)	 */
	msdc_ctlblk.dataptr[4] = (CBW_DEVICE_BLOCK_SIZE >> 24);
	msdc_ctlblk.dataptr[5] = (CBW_DEVICE_BLOCK_SIZE >> 16);
	msdc_ctlblk.dataptr[6] = (CBW_DEVICE_BLOCK_SIZE >> 8);
	msdc_ctlblk.dataptr[7] = (CBW_DEVICE_BLOCK_SIZE & 0xff);

	msdc_ctlblk.prostate = BULK_DATA_STA;

#if (USBD_BULKEP == BULK_EP12)
	drv_l1_usbd_send_bulk_in(msdc_ctlblk.dataptr, 8);
#elif (USBD_BULKEP == BULK_EP89)
	drv_l1_usbd_send_bulk89_in(msdc_ctlblk.dataptr, 8);
#endif
	msdc_ctlblk.cbwdatatcnt = 0;	//8 bytes send ok
	msdc_ctlblk.cswdataresidue = 0;
	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
}

/************************************************************************************
*   void drv_l2_usbd_scsi_cmd_read10(void)                                                 *
*   opcode: 0x28(40)                                                                *
*   purpose: Read command.                                                          *
*                                                                                   *                          *
*   USB DMA <--------> RAM buffer size <---------> MSDC SCSI sector length and size *
*                                                                                   *
*       e.g 512 bytes BOT_DMA_RAM_BUF_SIZE                  1 sector = 512 bytes    *
************************************************************************************/
void drv_l2_usbd_scsi_cmd_read10(void)
{
	MSDC_LOGIC_UNIT *lun_ptr;
	INT32U			cbw_scsi_lba, cbw_scsi_len;
	INT32S			ret = -1;

	if(drv_l2_usbd_msdc_check_lun_status() == USBD_RET_ERROR)
	{
		USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_scsi_cmd_read10 check lun fail, lun %d\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		return;
	}

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	USBD_L2MSDCLOG("USBDL2MSDC: enter drv_l2_usbd_scsi_cmd_read10, lun %d, type %d!!\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN, lun_ptr->luntype);

	/* Get current LBA and transfer length from CBW */
	drv_l2_usbd_msdc_get_read_lba_len(&cbw_scsi_lba, &cbw_scsi_len, 10);

	//print_string("USBDL2MSDC: cbw_scsi_lba %d cbw_scsi_len %d !!\r\n", cbw_scsi_lba, cbw_scsi_len);
	/* Check host/deivce length and dir, stall pipes and return CSW back, for USB 2.0 test suite */
	if((msdc_ctlblk.cbw.cbw_cb.bmCBWFlags >> 7) == CBW_DATA_DIR_OUT)
	{
		/* Flag = OUT */
		/* Ho <> Di */
		drv_l1_usbd_send_bulk_stall_null(CBW_DATA_DIR_OUT);

		/* Send CSW command back to host */
		msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PHASE_ERROR_STA;
		msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
		drv_l2_usbd_msdc_send_csw_data();
		return;
	}
	else
	{
		/* Flag = IN */
		if((cbw_scsi_len << 9) != msdc_ctlblk.cbwdatatcnt)
		{
			/* Hi > Dn, Hn < Di, Hi > Di */
			drv_l1_usbd_send_bulk_stall_null(CBW_DATA_DIR_IN);
			msdc_ctlblk.csw.bCSWStatus = CSW_CMD_FAILED_STA;
			msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
			drv_l2_usbd_msdc_send_csw_data();
			USBD_L2MSDCLOG("Read 10, cbwdatacnt %d, scsi len %d\r\n", msdc_ctlblk.cbwdatatcnt, cbw_scsi_len);
			return;
		}
	}

	if((cbw_scsi_lba + cbw_scsi_len) > lun_ptr->secSize)
	{
		scsi_send_stall_null();
		USBD_L2MSDCLOG("Read10 lab error!!\r\n");
		scsi_command_fail(0x1b);
		return;
	}

	/* Run read command phase */
	if(lun_ptr->storageaccess.usbd_msdc_read_cmd_phase != NULL)
	{
		ret = lun_ptr->storageaccess.usbd_msdc_read_cmd_phase(lun_ptr->storageaccess.priv, cbw_scsi_lba, cbw_scsi_len);
	}

	if(ret != 0)
	{
		msdc_ctlblk.cswdataresidue = cbw_scsi_len * CBW_DEVICE_BLOCK_SIZE;
		scsi_send_stall_null();
		scsi_command_fail(0x12);
		return;
	}

	//msdc_ctlblk.rambufcnt = cbw_scsi_len >> BOT_DMA_RAM_BUF_ORDER;    //how many ram buufer
	msdc_ctlblk.rambufcnt = cbw_scsi_len / (dmabufsize / MSDC_BLOCK_SIZE);			//how many ram buufer

	//msdc_ctlblk.rambufres = cbw_scsi_len & (BOT_DMA_RAM_BUF_SCALE-1); //last ram buffer size used
	msdc_ctlblk.rambufres = cbw_scsi_len & ((dmabufsize / MSDC_BLOCK_SIZE) - 1);	//last ram buffer size used

	/* Set DMA buffer's state */
	msdc_dma_buf[0].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;
	msdc_dma_buf[1].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;

	msdc_ctlblk.dmaptr = &msdc_dma_buf[0];				//assign dma buffer pointer
	msdc_ctlblk.prostate = BULK_DATA_STA;				//enter BOT DATA state
	msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_READ_10;	//Read10
#if (_OPERATING_SYSTEM != _OS_NONE)
	ret = -1;
	if(msdc_ctlblk.rambufcnt)
	{
		if(lun_ptr->storageaccess.usbd_msdc_read_dma_phase != NULL)
		{
			ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->dataptr, 1, (dmabufsize / MSDC_BLOCK_SIZE));
#if (_DRV_L1_CACHE == 1)
			cache_drain_range((INT32U) msdc_ctlblk.dmaptr->dataptr, (INT32U) dmabufsize);
#endif
		}

		msdc_ctlblk.dmaptr->usedbuffsize = dmabufsize;

		msdc_ctlblk.rambufcnt--;
		msdc_ctlblk.nodmadata = 0;
	}
	else
	if(msdc_ctlblk.rambufres)
	{
		if(lun_ptr->storageaccess.usbd_msdc_read_dma_phase != NULL)
		{
			ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->dataptr, 1, msdc_ctlblk.rambufres);
#if (_DRV_L1_CACHE == 1)
			cache_drain_range((INT32U) msdc_ctlblk.dmaptr->dataptr, (INT32U) msdc_ctlblk.rambufres);
#endif
		}

		msdc_ctlblk.dmaptr->usedbuffsize = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
		msdc_ctlblk.rambufres = 0;
		msdc_ctlblk.nodmadata = 0;
	}
	else
	{
		/* no DMA data to send */
		msdc_ctlblk.nodmadata = 1;
		if(lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase != NULL)
		{
			lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase(lun_ptr->storageaccess.priv);
		}
	}

	drv_l2_usbd_msdc_do_read_scsi_cmd();
#endif

	//DBG_PRINT("Read 10, cbw_scsi_lba %d, cbw_scsi_len %d, data len %d, rambufcnt %d, rambufres %d\r\n", cbw_scsi_lba, cbw_scsi_len, (cbw_scsi_len << 9), msdc_ctlblk.rambufcnt, msdc_ctlblk.rambufres);
	/* Doing DMA transfer for the whole SCSI length here. i.e, length 8, size = 4k */
	//drv_l1_usbd_send_bulk_in((void*)RAMDISK_Get_Start_LBA_Address(cbw_scsi_len), (cbw_scsi_len << 9));
}

/************************************************************************************
*   void drv_l2_usbd_scsi_cmd_write10(void)                                         *
*   opcode: 0x2a(42)                                                                *
*   purpose: Write command.                                                         *
*                                                                                   *
*   USB DMA <--------> RAM buffer size <---------> MSDC SCSI sector length and size *
*                                                                                   *
*       e.g 512 bytes BOT_DMA_RAM_BUF_SIZE                  1 sector = 512 bytes    *
************************************************************************************/
void drv_l2_usbd_scsi_cmd_write10(void)
{
	MSDC_LOGIC_UNIT *lun_ptr;
	INT32U			cbw_scsi_lba, cbw_scsi_len;
#if (_OPERATING_SYSTEM != _OS_NONE)
	INT32U			transfer_len;
#endif
	INT32S			ret = 0;

	if(drv_l2_usbd_msdc_check_lun_status() == USBD_RET_ERROR)
	{
		USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_scsi_cmd_write10 check lun fail, lun %d\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		return;
	}

	USBD_L2MSDCLOG("USBDL2MSDC: enter drv_l2_usbd_scsi_cmd_write10!!\r\n");

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	/* Get current LBA and transfer length from CBW */
	drv_l2_usbd_msdc_get_read_lba_len(&cbw_scsi_lba, &cbw_scsi_len, 10);

	USBD_L2MSDCLOG("USBDL2MSDC: Write 10 cbw_scsi_lba %d cbw_scsi_len %d !!\r\n", cbw_scsi_lba, cbw_scsi_len);

	/* Check flag, length and dir, if error, stall the pies, for USB 2.0 test suite */
	if((msdc_ctlblk.cbw.cbw_cb.bmCBWFlags >> 7) == CBW_DATA_DIR_IN)
	{
		/* Flag IN */
		/* Hi <> Do */
		drv_l1_usbd_send_bulk_stall_null(CBW_DATA_DIR_IN);

		/* Set CSW status failed */
		msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PHASE_ERROR_STA;
		msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
		drv_l2_usbd_msdc_send_csw_data();
		return;
	}
	else
	{
		/* Flag OUT */
		if(msdc_ctlblk.cbwdatatcnt == 0)
		{
			/* Hn < Do */
			msdc_ctlblk.csw.bCSWStatus = CSW_CMD_FAILED_STA;
			msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
			drv_l2_usbd_msdc_send_csw_data();
			return;
		}
		else
		{
			/* msdc_ctlblk.cbwdatatcnt > 0 */
			if(msdc_ctlblk.cbwdatatcnt > (cbw_scsi_len << 9))
			{
				/* Ho > Do */
				drv_l1_usbd_send_bulk_stall_null(CBW_DATA_DIR_OUT);
				msdc_ctlblk.csw.bCSWStatus = CSW_CMD_FAILED_STA;
				msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
				drv_l2_usbd_msdc_send_csw_data();
				return;
			}
			else
			if(msdc_ctlblk.cbwdatatcnt < (cbw_scsi_len << 9))
			{
				/* Ho < Do */
				drv_l1_usbd_send_bulk_stall_null(CBW_DATA_DIR_OUT);
				msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PHASE_ERROR_STA;
				msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
				drv_l2_usbd_msdc_send_csw_data();
				return;
			}
		}
	}

	msdc_ctlblk.nodmadata = 0;

	if((cbw_scsi_lba + cbw_scsi_len) > lun_ptr->secSize)
	{
		USBD_L2MSDCLOG("USBDL2MSDC: Write 10 lba error\r\n");
		scsi_send_stall_null();
		scsi_command_fail(0x1b);	/* LOGICAL BLOCK ADDRESS OUT OF RANGE */
		return;						/* skip this writing */
	}

	/* Run write command phase */
	if(lun_ptr->storageaccess.usbd_msdc_write_cmd_phase != NULL)
	{
		ret = lun_ptr->storageaccess.usbd_msdc_write_cmd_phase(lun_ptr->storageaccess.priv, cbw_scsi_lba, cbw_scsi_len);
	}

	if(ret != 0)
	{
		msdc_ctlblk.cswdataresidue = cbw_scsi_len * MSDC_BLOCK_SIZE;
		scsi_send_stall_null();
		scsi_command_fail(0x10);	/* WRITE FAULT */
		return;						/* skip this writing */
	}

	/* Set DMA buffer's state */
	msdc_dma_buf[0].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;
	msdc_dma_buf[1].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;

	msdc_ctlblk.dmaptr = &msdc_dma_buf[0];

	//msdc_ctlblk.rambufcnt = cbw_scsi_len >> BOT_DMA_RAM_BUF_ORDER;    	//how many ram buufer
	msdc_ctlblk.rambufcnt = cbw_scsi_len / (dmabufsize / MSDC_BLOCK_SIZE);			//how many ram buufer

	//msdc_ctlblk.rambufres = cbw_scsi_len & (BOT_DMA_RAM_BUF_SCALE-1); 	//last ram buffer size used
	msdc_ctlblk.rambufres = cbw_scsi_len & ((dmabufsize / MSDC_BLOCK_SIZE) - 1);	//last ram buffer size used
	msdc_ctlblk.prostate = BULK_DATA_STA;
	msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_WRITE_10;	//Write10

	//DBG_PRINT("Write10, cbw_scsi_lba %d, cbw_scsi_len %d, rambufcnt%d,  rambufres %d\r\n", cbw_scsi_lba, cbw_scsi_len,
	//msdc_ctlblk.rambufcnt, msdc_ctlblk.rambufres);
	/* Doing DMA transfer for whole SCSI sector length here. i.e length = 8, size = 4K */
	//drv_l1_usbd_rec_bulk_out((void*)RAMDISK_Get_Start_LBA_Address(cbw_scsi_len), transfer_len);	
#if (_OPERATING_SYSTEM != _OS_NONE)
	if(msdc_ctlblk.rambufcnt)
	{
		transfer_len = dmabufsize;
		msdc_ctlblk.secnum = (dmabufsize / MSDC_BLOCK_SIZE);
	}
	else
	if(msdc_ctlblk.rambufres)
	{
		transfer_len = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
		msdc_ctlblk.secnum = msdc_ctlblk.rambufres;
	}
	else
	{
		transfer_len = 0;
		msdc_ctlblk.secnum = 0;
		msdc_ctlblk.nodmadata = 1;
	}

	USBD_L2MSDCLOG("USBDL2MSDC: drv_l1_usbd_rec_bulk_out, ptr %p, transferlen %d !!\r\n", msdc_ctlblk.dmaptr->dataptr, transfer_len);
#if (USBD_BULKEP == BULK_EP12)
	drv_l1_usbd_rec_bulk_out((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len);					//Start OUT DMA
#elif (USBD_BULKEP == BULK_EP89)
	drv_l1_usbd_get_dma_bulk89_out_data_by_len((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len); //Start OUT DMA
#endif

	//DBG_PRINT("IN write 10\r\n");
	if(msdc_ctlblk.rambufcnt)
	{
		msdc_ctlblk.rambufcnt--;
	}
	else
	if(msdc_ctlblk.rambufres)
	{
		msdc_ctlblk.rambufres = 0;
	}

#endif
}

void drv_l2_usbd_scsi_cmd_mediumremoval(void)
{
	if(msdc_ctlblk.cbw.cbw_cb.CBWCB[4] & BIT0)
	{
		msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN].senseidx = 0x1c; /* INVALID FIELD IN COMMAND PACKET */
		msdc_ctlblk.csw.bCSWStatus = CSW_CMD_FAILED_STA;
	}
	else
	{
		msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN].senseidx = 0;
		msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
	}

	msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt;
	drv_l2_usbd_msdc_send_csw_data();
}

void drv_l2_usbd_scsi_cmd_requestsense(void)
{
	INT32U	senseidx = msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN].senseidx;
	INT8U	len = msdc_ctlblk.cbw.cbw_cb.CBWCB[4];
	INT8U	tlen = 18;	/* request sense response length */

	USBD_L2MSDCLOG("USBDL2MSDC: requestsense, lun %d, senseidx 0x%x\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN, senseidx);

	//DBG_PRINT("USBDL2MSDC: requestsense, lun %d, senseidx 0x%x\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN, senseidx);
	/* If dCBWDataTransferLength is 0, send CBW back to host, USB 2.0 test suite */
	if(msdc_ctlblk.cbwdatatcnt == 0)
	{
		msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
		msdc_ctlblk.cswdataresidue = 0;
		drv_l2_usbd_msdc_send_csw_data();
		return;
	}

	msdc_ctlblk.dataptr[0] = 0xF0;
	msdc_ctlblk.dataptr[1] = 0x00;
	msdc_ctlblk.dataptr[2] = scsi_sense_tbl[senseidx].sensekey;
	msdc_ctlblk.dataptr[3] = 0x00;
	msdc_ctlblk.dataptr[4] = 0x00;
	msdc_ctlblk.dataptr[5] = 0x00;
	msdc_ctlblk.dataptr[6] = 0x00;
	msdc_ctlblk.dataptr[7] = 0x0A;
	msdc_ctlblk.dataptr[8] = 0x00;
	msdc_ctlblk.dataptr[9] = 0x00;
	msdc_ctlblk.dataptr[10] = 0x00;
	msdc_ctlblk.dataptr[11] = 0x00;
	msdc_ctlblk.dataptr[12] = scsi_sense_tbl[senseidx].asc;
	msdc_ctlblk.dataptr[13] = scsi_sense_tbl[senseidx].ascq;
	msdc_ctlblk.dataptr[14] = 0x00;
	msdc_ctlblk.dataptr[15] = 0x00;
	msdc_ctlblk.dataptr[16] = 0x00;
	msdc_ctlblk.dataptr[17] = 0x00;

	if(msdc_ctlblk.cbwdatatcnt < len)
		len = msdc_ctlblk.cbwdatatcnt;
	if(len < tlen)
		tlen = len;

	msdc_ctlblk.prostate = BULK_DATA_STA;
#if (USBD_BULKEP == BULK_EP12)
	drv_l1_usbd_send_bulk_in(msdc_ctlblk.dataptr, tlen);
#elif (USBD_BULKEP == BULK_EP89)
	drv_l1_usbd_send_bulk89_in(msdc_ctlblk.dataptr, tlen);
#endif
	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
	msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt - tlen;
	msdc_ctlblk.cbwdatatcnt = msdc_ctlblk.cswdataresidue;
}

/***********************************************************************
*   void drv_l2_usbd_scsi_cmd_formatcapacity(void)                     *
*   opcode: 0x23(35)                                                   *
*   purpose: Return the possible capacities that can be formatted on   * 
*            the currently installed medium.                           *  
*                                                                      *
*                                                                      *  
***********************************************************************/
void drv_l2_usbd_scsi_cmd_formatcapacity(void)
{
	MSDC_LOGIC_UNIT *lun_ptr;

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	if(drv_l2_usbd_msdc_check_lun_status() == USBD_RET_ERROR)
	{
		USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_scsi_cmd_formatcapacity check lun fail, lun %d\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
		return;
	}

	USBD_L2MSDCLOG("USBDL2MSDC: drv_l2_usbd_scsi_cmd_formatcapacity ok, lunnum %d, secsize %d!!\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN, lun_ptr->secSize);

	/* 0~2 Reserved, 3 Capacity List Length */
	msdc_ctlblk.dataptr[0] = 0x00;
	msdc_ctlblk.dataptr[1] = 0x00;
	msdc_ctlblk.dataptr[2] = 0x00;
	msdc_ctlblk.dataptr[3] = 0x10;

	/* 4~7 Number of Blocks, 8 Descriptor Code, 9~11 Block Length */
	msdc_ctlblk.dataptr[4] = lun_ptr->secSize >> 24;
	msdc_ctlblk.dataptr[5] = lun_ptr->secSize >> 16;
	msdc_ctlblk.dataptr[6] = lun_ptr->secSize >> 8;
	msdc_ctlblk.dataptr[7] = lun_ptr->secSize;

	msdc_ctlblk.dataptr[8] = 0x02;

	msdc_ctlblk.dataptr[9] = 0x00;
	msdc_ctlblk.dataptr[10] = 0x02;
	msdc_ctlblk.dataptr[11] = 0x00;

	/* 12~15 Number of Blocks, 16 Descriptor Code, 17~19 Block Length */
	msdc_ctlblk.dataptr[12] = lun_ptr->secSize >> 24;
	msdc_ctlblk.dataptr[13] = lun_ptr->secSize >> 16;
	msdc_ctlblk.dataptr[14] = lun_ptr->secSize >> 8;
	msdc_ctlblk.dataptr[15] = lun_ptr->secSize;

	msdc_ctlblk.dataptr[16] = 0x00;

	msdc_ctlblk.dataptr[17] = 0x00;
	msdc_ctlblk.dataptr[18] = 0x02;
	msdc_ctlblk.dataptr[19] = 0x00;

	msdc_ctlblk.prostate = BULK_DATA_STA;
#if (USBD_BULKEP == BULK_EP12)
	drv_l1_usbd_send_bulk_in(msdc_ctlblk.dataptr, 20);
#elif (USBD_BULKEP == BULK_EP89)
	drv_l1_usbd_send_bulk89_in(msdc_ctlblk.dataptr, 20);
#endif
	msdc_ctlblk.cbwdatatcnt = 0;	//20 bytes send ok
	msdc_ctlblk.cswdataresidue = 0;
	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
}

/***********************************************************************
*   void drv_l2_usbd_scsi_cmd_inquiry(void)                            *
*   opcode: 0x12(18)                                                   *
*   purpose: Return the information regarding the logical unit         * 
*            and SCSI target device be sent to the application client. * 
*                                                                      *
*                                                                      *  
***********************************************************************/
void drv_l2_usbd_scsi_cmd_inquiry(void)
{
	INT32U			datacnt;
	MSDC_LOGIC_UNIT *lun_ptr;

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	/* Check dCBWDataransferLength if 0 or not,for USB 2.0 test suite*/
	if(msdc_ctlblk.cbwdatatcnt == 0)
	{
		msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
		msdc_ctlblk.cswdataresidue = 0;
		drv_l2_usbd_msdc_send_csw_data();
		return;
	}
	else
	if(msdc_ctlblk.cbwdatatcnt > BOT_SCSI_InquiryLength)
	{
		datacnt = BOT_SCSI_InquiryLength;
	}
	else
	{
		datacnt = msdc_ctlblk.cbwdatatcnt;
	}

	if(lun_ptr->luntype == LUN_CDROM_TYPE)
	{
		memcpy((INT8S *) msdc_ctlblk.dataptr, (INT8S *) msdc_ctlblk.SCSIinquiryCDdataptr, datacnt);
	}
	else
	{
		memcpy((INT8S *) msdc_ctlblk.dataptr, (INT8S *) msdc_ctlblk.SCSIinquirydataptr, datacnt);
	}

	msdc_ctlblk.prostate = BULK_DATA_STA;	/* enter BOT DATA state */
#if (USBD_BULKEP == BULK_EP12)
	drv_l1_usbd_send_bulk_in(msdc_ctlblk.dataptr, datacnt);
#elif (USBD_BULKEP == BULK_EP89)
	drv_l1_usbd_send_bulk89_in(msdc_ctlblk.dataptr, datacnt);
#endif
	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
	if(msdc_ctlblk.cbwdatatcnt > BOT_SCSI_InquiryLength)
	{
		msdc_ctlblk.cswdataresidue = 0;
		msdc_ctlblk.cbwdatatcnt = 0;

		//DBG_PRINT("11111==msdc_ctlblk.cbwdatatcnt = %d\r\n", msdc_ctlblk.cbwdatatcnt);
	}
	else
	{
		msdc_ctlblk.cswdataresidue = msdc_ctlblk.cbwdatatcnt - datacnt;
		msdc_ctlblk.cbwdatatcnt = msdc_ctlblk.cswdataresidue;

		//DBG_PRINT("22222==msdc_ctlblk.cbwdatatcnt = %d\r\n", msdc_ctlblk.cbwdatatcnt);
	}
}

/***********************************************************************
*   void drv_l2_usbd_scsi_cmd_verify(void)                             *
*   opcode: 0x12(18)                                                   *
*   purpose: Return the information regarding the logical unit         * 
*            and SCSI target device be sent to the application client. * 
*                                                                      *
*                                                                      *  
***********************************************************************/
void drv_l2_usbd_scsi_cmd_verify(void)
{
	msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN].senseidx = 0;	//No Sense
	msdc_ctlblk.cbwdatatcnt = 0;

	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
	msdc_ctlblk.cswdataresidue = 0;
	drv_l2_usbd_msdc_send_csw_data();
}

/***********************************************************************
   void drv_l2_usbd_scsi_cmd_startstop(void)                           *
   opcode: 0x1b(27)                                                    *
   purpose: Check CBWCB[5] bit0 & bit1                                 *
                           (Start) & (LoEj)                            *  
***********************************************************************/
void drv_l2_usbd_scsi_cmd_startstop(void)
{
	MSDC_LOGIC_UNIT *lun_ptr;
	INT8U			LoEj_Start = msdc_ctlblk.cbw.cbw_cb.CBWCB[4] & 0x03;

	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	lun_ptr->status = LUNSTS_START_STOP_NOMEDIA;

	DBG_PRINT("Start Stop unit, LoEj_Start = 0x%x \r\n", LoEj_Start);

	msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN].senseidx = 0;	//No Sense
	msdc_ctlblk.cbwdatatcnt = 0;

	msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;
	msdc_ctlblk.cswdataresidue = 0;
	drv_l2_usbd_msdc_send_csw_data();
}

/******************************************************
    Function declaration
******************************************************/
void drv_l2_usbd_msdc_check_start_stop_unit(void)
{
	MSDC_LOGIC_UNIT		*lun_ptr;
	INT32S				ret = -1;
	STORAGE_DRV_INFO	inf;
	INT32U				lunnum = 0;

	for(lunnum = 0; lunnum < MAX_MSDC_LUN_NUM; lunnum++)
	{
		lun_ptr = &msdc_ctlblk.lunptr[lunnum];

		/* check no media from SCSI command 0x1B START STOP UNIT */
		if(lun_ptr->status == LUNSTS_START_STOP_NOMEDIA)
		{
			ret = -1;

			if(lun_ptr->storageaccess.usbd_msdc_init != NULL)
			{
				ret = lun_ptr->storageaccess.usbd_msdc_init(lun_ptr->storageaccess.priv);
			}

			if(!ret && lun_ptr->storageaccess.usbd_msdc_getdrvinfo != NULL)
			{
				lun_ptr->storageaccess.usbd_msdc_getdrvinfo(lun_ptr->storageaccess.priv, &inf);
				lun_ptr->secSize = (INT32U) inf.nSectors;
				lun_ptr->status = (LUNSTS_NORMAL | LUNSTS_MEDIACHANGE);
				DBG_PRINT("Storage LUN %d start stop unit!!\r\n", lunnum);
			}
			else
			{
				/* Storage init faile */
				lun_ptr->secSize = 0;
				lun_ptr->status = LUNSTS_NOMEDIA;
				if(lun_ptr->storageaccess.usbd_msdc_uninit != NULL)
				{
					lun_ptr->storageaccess.usbd_msdc_uninit(lun_ptr->storageaccess.priv);
				}

				DBG_PRINT("Storage LUN %d start stop unit, but init failed!!\r\n", lunnum);
			}
		}
	}
}

void drv_l2_usbd_msdc_process_insertion(void)
{
	MSDC_LOGIC_UNIT		*lun_ptr;						// = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];
	INT32S				ret = -1;
	STORAGE_DRV_INFO	inf;
	INT32U				lunnum = 0;

	for(lunnum = 0; lunnum < MAX_MSDC_LUN_NUM; lunnum++)
	{
		lun_ptr = &msdc_ctlblk.lunptr[lunnum];

		if(lun_ptr->storageaccess.usbd_msdc_insert_event != NULL)
		{
			/* process insertion event */
			ret = lun_ptr->storageaccess.usbd_msdc_insert_event(lun_ptr->storageaccess.priv);

			if(!ret)
			{
				/* storage return success */
				if(lun_ptr->status == LUNSTS_NOMEDIA)
				{
					/* previous is no media, reinit storage now */
					ret = -1;

					if(lun_ptr->storageaccess.usbd_msdc_init != NULL)
					{
						ret = lun_ptr->storageaccess.usbd_msdc_init(lun_ptr->storageaccess.priv);
					}

					if(!ret && lun_ptr->storageaccess.usbd_msdc_getdrvinfo != NULL)
					{
						lun_ptr->storageaccess.usbd_msdc_getdrvinfo(lun_ptr->storageaccess.priv, &inf);
						lun_ptr->secSize = (INT32U) inf.nSectors;
						lun_ptr->status = (LUNSTS_NORMAL | LUNSTS_MEDIACHANGE);
						DBG_PRINT("Storage LUN %d inserted!!\r\n", lunnum);
					}
					else
					{
						/* Storage init faile */
						lun_ptr->secSize = 0;
						lun_ptr->status = LUNSTS_NOMEDIA;
						if(lun_ptr->storageaccess.usbd_msdc_uninit != NULL)
						{
							lun_ptr->storageaccess.usbd_msdc_uninit(lun_ptr->storageaccess.priv);
						}

						DBG_PRINT("Storage LUN %d inserted, but init failed!!\r\n", lunnum);
					}
				}
			}
			else
			{
				/* storage return fail */
				if(lun_ptr->status == LUNSTS_NORMAL)
				{
					/* previous is ok, set state into no media state */
					lun_ptr->secSize = 0;
					lun_ptr->status = (LUNSTS_NOMEDIA | LUNSTS_MEDIACHANGE);
					DBG_PRINT("Storage LUN %d removed!!\r\n", lunnum);
				}
			}
		}
	}
}

void drv_l2_usbd_msdc_process_storage(void)
{
	MSDC_LOGIC_UNIT *lun_ptr;
	INT32S			ret = -1;
	INT32U			transfer_len;
	INT32U			CurDMABufState;
	INT32U			CurdoingSCSIreadwrite = msdc_ctlblk.doingSCSIreadwrite;

	if(CurdoingSCSIreadwrite != DO_SCSI_READ_10 && CurdoingSCSIreadwrite != DO_SCSI_WRITE_10)
	{
		/* nothign to do */
		return;
	}

	/* Get current LUN */
	lun_ptr = &msdc_ctlblk.lunptr[msdc_ctlblk.cbw.cbw_cb.bCBWLUN];

	/* check LUN status */
	if(lun_ptr->status & LUNSTS_MEDIACHANGE)
	{
		msdc_ctlblk.rambufcnt = 0;
		msdc_ctlblk.rambufres = 0;
		scsi_send_stall_null();
		if(CurdoingSCSIreadwrite == DO_SCSI_READ_10)
		{
			scsi_command_fail(0x12);					/* UNRECOVERD READ ERROR */
		}
		else
		if(CurdoingSCSIreadwrite == DO_SCSI_WRITE_10)
		{
			scsi_command_fail(0x10);					/* WRITE FAULT */
		}

		msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_NONE;	/* reset read/write flag */
		return;
	}

	/* Check A/B buffer DMA status for BULK IN/OUT DMA */
	if(dmadone)
	{
		if(dmadone == DMA_BULK_IN_MODE)
		{
			if(msdc_ctlblk.dmaptr->DMABufState == SCSI_DMA_BUF_INING_STA)
			{
				//DBG_PRINT("BI done now, sta[%d][%d]\r\n", msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState);
				msdc_ctlblk.dmaptr->DMABufState = SCSI_DMA_BUF_IN_DONE_STA;
			}
		}
		else
		if(dmadone == DMA_BULK_OUT_MODE)
		{
			if(msdc_ctlblk.dmaptr->DMABufState == SCSI_DMA_BUF_OUTING_STA)
			{
				//DBG_PRINT("BO done now, sta[%d][%d]\r\n", msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState);
				msdc_ctlblk.dmaptr->DMABufState = SCSI_DMA_BUF_OUT_DONE_STA;
			}
			else
			if(msdc_ctlblk.dmaptr->next->DMABufState == SCSI_DMA_BUF_OUTING_STA)
			{
				//DBG_PRINT("BO done next, sta[%d][%d]\r\n", msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState);
				msdc_ctlblk.dmaptr->next->DMABufState = SCSI_DMA_BUF_OUT_DONE_STA;
			}
		}

		dmadone = 0;
	}

	CurDMABufState = msdc_ctlblk.dmaptr->DMABufState;

	/* process storage side state */
	if(CurDMABufState == SCSI_DMA_BUF_NOT_USED_STA)
	{
		//DBG_PRINT("bufcnt %d, rescnt %d\r\n", msdc_ctlblk.rambufcnt, msdc_ctlblk.rambufres);
		if(CurdoingSCSIreadwrite == DO_SCSI_READ_10)
		{
			/* Process read10 */
			//DBG_PRINT("Read 10, lun %d\r\n", msdc_ctlblk.cbw.cbw_cb.bCBWLUN);
			if(lun_ptr->storageaccess.usbd_msdc_read_dma_phase != NULL)
			{
				if(msdc_ctlblk.rambufcnt)
				{
					//DBG_PRINT("MSDC start read dma phase 111\r\n");
					ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->dataptr, 1, (dmabufsize / MSDC_BLOCK_SIZE));
#if (_DRV_L1_CACHE == 1)
					cache_drain_range((INT32U) msdc_ctlblk.dmaptr->dataptr, (INT32U) dmabufsize);
#endif
					if(ret != 0)
					{
						msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_NONE;
						if(lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase != NULL)
						{
							lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase(lun_ptr->storageaccess.priv);
						}

						scsi_send_stall_null();
						scsi_command_fail(0x12);
						return;
					}
				}
				else
				if(msdc_ctlblk.rambufres)
				{
					//DBG_PRINT("MSDC start read dma phase 222\r\n");
					ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->dataptr, 1, msdc_ctlblk.rambufres);
#if (_DRV_L1_CACHE == 1)
					cache_drain_range((INT32U) msdc_ctlblk.dmaptr->dataptr, (INT32U) msdc_ctlblk.rambufres);
#endif
					if(ret != 0)
					{
						msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_NONE;
						if(lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase != NULL)
						{
							lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase(lun_ptr->storageaccess.priv);
						}

						scsi_send_stall_null();
						scsi_command_fail(0x12);
						return;
					}
				}

				/* Reading done */
				/* DMA done */
				if(msdc_ctlblk.rambufcnt)
				{
					msdc_ctlblk.dmaptr->usedbuffsize = dmabufsize;
				}
				else
				if(msdc_ctlblk.rambufres)
				{
					msdc_ctlblk.dmaptr->usedbuffsize = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
				}

				msdc_ctlblk.dmaptr->DMABufState = SCSI_DMA_BUF_READ_DONE_STA;

				//DBG_PRINT("First READ 10, sta[%d][%d]\r\n", msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState);
			}
		}
		else
		if(CurdoingSCSIreadwrite == DO_SCSI_WRITE_10)
		{
			/* Process write10 */
			if(msdc_ctlblk.rambufcnt)
			{
				transfer_len = dmabufsize;
				msdc_ctlblk.secnum = (dmabufsize / MSDC_BLOCK_SIZE);
				msdc_ctlblk.dmaptr->DMABufState = SCSI_DMA_BUF_OUTING_STA;

				//DBG_PRINT("BO now\r\n");
#if (USBD_BULKEP == BULK_EP12)
				drv_l1_usbd_rec_bulk_out((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len);
#elif (USBD_BULKEP == BULK_EP89)
				drv_l1_usbd_get_dma_bulk89_out_data_by_len((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len);
#endif
			}
			else
			if(msdc_ctlblk.rambufres)
			{
				transfer_len = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
				msdc_ctlblk.secnum = msdc_ctlblk.rambufres;
				msdc_ctlblk.dmaptr->DMABufState = SCSI_DMA_BUF_OUTING_STA;

				//DBG_PRINT("BO1 now\r\n");
#if (USBD_BULKEP == BULK_EP12)
				drv_l1_usbd_rec_bulk_out((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len);
#elif (USBD_BULKEP == BULK_EP89)
				drv_l1_usbd_get_dma_bulk89_out_data_by_len((void *) msdc_ctlblk.dmaptr->dataptr, transfer_len);
#endif
			}
		}
	}
	else
	if(CurDMABufState == SCSI_DMA_BUF_WRITE_DONE_STA)
	{
		/* Calculate */
		if(msdc_ctlblk.rambufcnt)
		{
			msdc_ctlblk.rambufcnt--;
		}
		else
		if(msdc_ctlblk.rambufres)
		{
			msdc_ctlblk.rambufres = 0;
		}

		if(msdc_ctlblk.rambufcnt == 0 && msdc_ctlblk.rambufres == 0)
		{
			//DBG_PRINT("Enter Write CBW_state\r\n");
			if(lun_ptr->storageaccess.usbd_msdc_write_cmdend_phase != NULL)
				lun_ptr->storageaccess.usbd_msdc_write_cmdend_phase(lun_ptr->storageaccess.priv);

			/* reset the polling flag and state machine */
			msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_NONE;
			msdc_dma_buf[0].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;
			msdc_dma_buf[1].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;

			/* Enter CBW state */
			msdc_ctlblk.cswdataresidue = 0;
			msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;

			/* send CSW */
			//DBG_PRINT("send CSW for write10.....\r\n");
			drv_l2_usbd_msdc_send_csw_data();			/* triggle next CBW */
		}
		else
		{
			msdc_ctlblk.dmaptr = msdc_ctlblk.dmaptr->next;

			//DBG_PRINT("CurDMA buf write done change dmaptr to next, rambufcnt = %d, sta[%d][%d]\r\n", msdc_ctlblk.rambufcnt, msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState);
		}
	}
	else
	if(CurDMABufState == SCSI_DMA_BUF_READ_DONE_STA)
	{
		//DBG_PRINT("SCSI_DMA_BUF_READ_DONE_STA, cnt = %d, size 0x%x\r\n", msdc_ctlblk.rambufcnt, msdc_ctlblk.dmaptr->usedbuffsize);
		/* Sending IN data */
		msdc_ctlblk.dmaptr->DMABufState = SCSI_DMA_BUF_INING_STA;

		//DBG_PRINT("BI now, size = %d, sta[%d][%d], bcnt[%d][%d], buf 0x%x\r\n", msdc_ctlblk.dmaptr->usedbuffsize,msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState, msdc_ctlblk.rambufcnt, msdc_ctlblk.rambufres, msdc_ctlblk.dmaptr->dataptr);
#if (USBD_BULKEP == BULK_EP12)
		drv_l1_usbd_send_bulk_in((INT32U *) msdc_ctlblk.dmaptr->dataptr, msdc_ctlblk.dmaptr->usedbuffsize);
#elif (USBD_BULKEP == BULK_EP89)
		drv_l1_usbd_send_bulk89_in((INT32U *) msdc_ctlblk.dmaptr->dataptr, msdc_ctlblk.dmaptr->usedbuffsize);
#endif

		//if(msdc_ctlblk.dmaptr->next->DMABufState == SCSI_DMA_BUF_NOT_USED_STA)
		if(msdc_ctlblk.dmaptr->next->DMABufState == SCSI_DMA_BUF_NOT_USED_STA || msdc_ctlblk.dmaptr->next->DMABufState == SCSI_DMA_BUF_IN_DONE_STA)
		{
			/* Check and read data from storage to the next DMA buffer */
			if(msdc_ctlblk.rambufcnt > 1)
			{
				ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->next->dataptr, 1, (dmabufsize / MSDC_BLOCK_SIZE));
#if (_DRV_L1_CACHE == 1)
				cache_drain_range((INT32U) msdc_ctlblk.dmaptr->next->dataptr, (INT32U) dmabufsize);
#endif
				msdc_ctlblk.dmaptr->next->DMABufState = SCSI_DMA_BUF_READ_DONE_STA;
				if(ret != 0)
				{
					msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_NONE;
					if(lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase != NULL)
					{
						lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase(lun_ptr->storageaccess.priv);
					}

					scsi_send_stall_null();
					scsi_command_fail(0x12);			/* Read fault */
					return;
				}
			}
			else
			if(msdc_ctlblk.rambufcnt == 1 && msdc_ctlblk.rambufres > 0)
			{
				ret = lun_ptr->storageaccess.usbd_msdc_read_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->next->dataptr, 1, msdc_ctlblk.rambufres);
#if (_DRV_L1_CACHE == 1)
				cache_drain_range((INT32U) msdc_ctlblk.dmaptr->next->dataptr, (INT32U) msdc_ctlblk.rambufres);
#endif
				msdc_ctlblk.dmaptr->next->DMABufState = SCSI_DMA_BUF_READ_DONE_STA;
				if(ret != 0)
				{
					msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_NONE;
					if(lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase != NULL)
					{
						lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase(lun_ptr->storageaccess.priv);
					}

					scsi_send_stall_null();
					scsi_command_fail(0x12);			/* Read fault */
					return;
				}
			}
		}
	}
	else
	if(CurDMABufState == SCSI_DMA_BUF_IN_DONE_STA)
	{
		/* Calculate the data count */
		if(msdc_ctlblk.rambufcnt)
		{
			msdc_ctlblk.rambufcnt--;
		}
		else
		if(msdc_ctlblk.rambufres)
		{
			msdc_ctlblk.rambufres = 0;
		}

		if(msdc_ctlblk.rambufcnt == 0 && msdc_ctlblk.rambufres == 0)
		{
			//DBG_PRINT("Enter Read CBW_state\r\n");
			if(lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase != NULL)
				lun_ptr->storageaccess.usbd_msdc_read_cmdend_phase(lun_ptr->storageaccess.priv);

			/* reset the polling flag and state machine */
			msdc_ctlblk.doingSCSIreadwrite = DO_SCSI_NONE;
			msdc_dma_buf[0].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;
			msdc_dma_buf[1].DMABufState = SCSI_DMA_BUF_NOT_USED_STA;

			/* Enter CBW state */
			msdc_ctlblk.cswdataresidue = 0;
			msdc_ctlblk.csw.bCSWStatus = CSW_CMD_PASS_STA;

			/* send CSW */
			//DBG_PRINT("Send CSW...\r\n");
			drv_l2_usbd_msdc_send_csw_data();			/* triggle next CBW */
		}
		else
		{
			/* point to next DMA buffer pointer */
			msdc_ctlblk.dmaptr = msdc_ctlblk.dmaptr->next;
			if(msdc_ctlblk.rambufcnt)
			{
				msdc_ctlblk.dmaptr->usedbuffsize = dmabufsize;
			}
			else
			if(msdc_ctlblk.rambufres)
			{
				msdc_ctlblk.dmaptr->usedbuffsize = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
			}

			//DBG_PRINT("switch DMA pointer, size = %d, sta[%d][%d]\r\n", msdc_ctlblk.dmaptr->usedbuffsize,msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState);
		}
	}
	else
	if(CurDMABufState == SCSI_DMA_BUF_OUT_DONE_STA)
	{
		/* OUT DMA DONE, write into storage */
		if(lun_ptr->storageaccess.usbd_msdc_write_dma_phase != NULL)
		{
#if (_DRV_L1_CACHE == 1)
			cache_invalid_range((INT32U) msdc_ctlblk.dmaptr->dataptr, (msdc_ctlblk.secnum << 9));
#endif
			ret = lun_ptr->storageaccess.usbd_msdc_write_dma_phase(lun_ptr->storageaccess.priv, (INT32U *) msdc_ctlblk.dmaptr->dataptr, 1, msdc_ctlblk.secnum);
			msdc_ctlblk.dmaptr->DMABufState = SCSI_DMA_BUF_WRITE_DONE_STA;

			if(ret != 0)
			{
				if(lun_ptr->storageaccess.usbd_msdc_write_cmdend_phase != NULL)
				{
					lun_ptr->storageaccess.usbd_msdc_write_cmdend_phase(lun_ptr->storageaccess.priv);
				}

				scsi_send_stall_null();
				scsi_command_fail(0x10);				/* WRITE FAULT */
				return;
			}

			/* Do next out DMA */
			if(msdc_ctlblk.dmaptr->next->DMABufState == SCSI_DMA_BUF_WRITE_DONE_STA || msdc_ctlblk.dmaptr->next->DMABufState == SCSI_DMA_BUF_NOT_USED_STA)
			{
				if(msdc_ctlblk.rambufcnt > 1)
				{
					transfer_len = dmabufsize;
					msdc_ctlblk.secnum = (dmabufsize / MSDC_BLOCK_SIZE);
					msdc_ctlblk.dmaptr->next->DMABufState = SCSI_DMA_BUF_OUTING_STA;

					//DBG_PRINT("BO next, sta[%d][%d], rambufcnt =[%d]\r\n", msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState, msdc_ctlblk.rambufcnt);
#if (USBD_BULKEP == BULK_EP12)
					drv_l1_usbd_rec_bulk_out((void *) msdc_ctlblk.dmaptr->next->dataptr, transfer_len);
#elif (USBD_BULKEP == BULK_EP89)
					drv_l1_usbd_get_dma_bulk89_out_data_by_len((void *) msdc_ctlblk.dmaptr->next->dataptr, transfer_len);
#endif
				}
				else
				if(msdc_ctlblk.rambufcnt == 1 && msdc_ctlblk.rambufres > 0)
				{
					transfer_len = msdc_ctlblk.rambufres * MSDC_BLOCK_SIZE;
					msdc_ctlblk.secnum = msdc_ctlblk.rambufres;
					msdc_ctlblk.dmaptr->next->DMABufState = SCSI_DMA_BUF_OUTING_STA;

					//DBG_PRINT("BO1 next, sta[%d][%d]\r\n", msdc_ctlblk.dmaptr->DMABufState,msdc_ctlblk.dmaptr->next->DMABufState);
#if (USBD_BULKEP == BULK_EP12)
					drv_l1_usbd_rec_bulk_out((void *) msdc_ctlblk.dmaptr->next->dataptr, transfer_len);
#elif (USBD_BULKEP == BULK_EP89)
					drv_l1_usbd_get_dma_bulk89_out_data_by_len((void *) msdc_ctlblk.dmaptr->next->dataptr, transfer_len);
#endif
				}
			}
		}
	}
}

MSDC_LOGIC_UNIT *drv_l2_usbd_msdc_get_lun_by_type(INT8U type)
{
	INT8U	i;

	for(i = 0; i < MAX_MSDC_LUN_NUM; i++)
	{
		if(g_arlun[i].luntype == type)
			return &g_arlun[i];
	}

	return NULL;
}

INT32S drv_l2_usbd_msdc_set_lun(INT8U type, INT8U lunnum, INT32U wprotect, const MSDC_LUN_STORAGE_DRV *plun)
{
	if(lunnum >= MAX_MSDC_LUN_NUM)
	{
		return STATUS_FAIL;
	}

	g_arlun[lunnum].isused = 1;
	g_arlun[lunnum].luntype = type;

	/* Set write protect */
	if(wprotect)
	{
		g_arlun[lunnum].wprotect = 1;
	}

	/* init storage access functions table */
	g_arlun[lunnum].storageaccess.priv = plun->priv;
	g_arlun[lunnum].storageaccess.usbd_msdc_init = plun->usbd_msdc_init;
	g_arlun[lunnum].storageaccess.usbd_msdc_uninit = plun->usbd_msdc_uninit;
	g_arlun[lunnum].storageaccess.usbd_msdc_insert_event = plun->usbd_msdc_insert_event;
	g_arlun[lunnum].storageaccess.usbd_msdc_getdrvinfo = plun->usbd_msdc_getdrvinfo;
	g_arlun[lunnum].storageaccess.usbd_msdc_read_cmd_phase = plun->usbd_msdc_read_cmd_phase;
	g_arlun[lunnum].storageaccess.usbd_msdc_read_dma_phase = plun->usbd_msdc_read_dma_phase;
	g_arlun[lunnum].storageaccess.usbd_msdc_read_cmdend_phase = plun->usbd_msdc_read_cmdend_phase;
	g_arlun[lunnum].storageaccess.usbd_msdc_write_cmd_phase = plun->usbd_msdc_write_cmd_phase;
	g_arlun[lunnum].storageaccess.usbd_msdc_write_dma_phase = plun->usbd_msdc_write_dma_phase;
	g_arlun[lunnum].storageaccess.usbd_msdc_write_cmdend_phase = plun->usbd_msdc_write_cmdend_phase;
	g_arlun[lunnum].storageaccess.usbd_msdc_storage_dma_check_state = plun->usbd_msdc_storage_dma_check_state;

	return STATUS_OK;
}

INT32S drv_l2_usbd_msdc_reset_lun(INT8U lunnum)
{
	if(lunnum >= MAX_MSDC_LUN_NUM)
		return STATUS_FAIL;

	g_arlun[lunnum].senseidx = 0;
	g_arlun[lunnum].secSize = 0;
	g_arlun[lunnum].secSize = 0;
	g_arlun[lunnum].luntype = 0;
	g_arlun[lunnum].isused = 0;

	/* init storage access functions table */
	g_arlun[lunnum].storageaccess.priv = (void *) 0;
	g_arlun[lunnum].storageaccess.usbd_msdc_init = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_uninit = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_insert_event = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_getdrvinfo = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_read_cmd_phase = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_read_dma_phase = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_read_cmdend_phase = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_write_cmd_phase = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_write_dma_phase = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_write_cmdend_phase = NULL;
	g_arlun[lunnum].storageaccess.usbd_msdc_storage_dma_check_state = NULL;

	return STATUS_OK;
}

INT8U drv_l2_usbd_msdc_get_max_lun(void)
{
	if(msdc_ctlblk.maxlun)
		return(msdc_ctlblk.maxlun - 1);					/* maxlun is 0 base */
	else
		return msdc_ctlblk.maxlun;
}

/* For registeration of SCSI inquiry data */
void drv_l2_usbd_msdc_register_scsi_inquiry_data(INT8U *dataptr, INT8U *cddataptr)
{
	if(dataptr != NULL)
	{
		msdc_ctlblk.SCSIinquirydataptr = dataptr;
	}

	if(cddataptr != NULL)
	{
		msdc_ctlblk.SCSIinquiryCDdataptr = cddataptr;
	}
}

/* Register SCSI vendor command function */
void drv_l2_usbd_msdc_register_scsi_vendor_cmd_fn(SCSI_VENDOR_CMD_FN fn)
{
	msdc_ctlblk.process_scsi_vendor_cmd = fn;
}

void drv_l2_usbd_msdc_set_dma_buffer_size(INT32U size)
{
	dmabufsize = size;
}

INT32S drv_l2_usbd_msdc_init_lun(void)
{
	STORAGE_DRV_INFO	inf;
	INT32S				ret;
	INT8U				i;

	msdc_ctlblk.maxlun = 0;

	for(i = 0; i < MAX_MSDC_LUN_NUM; i++)
	{
		MSDC_LOGIC_UNIT *lun_ptr = &msdc_ctlblk.lunptr[i];

		gp_memset((INT8S *) &inf, 0, sizeof(inf));

		ret = STATUS_OK;

		if(lun_ptr->isused)
		{
			/* run related initialization procedure */
			msdc_ctlblk.maxlun++;

			/* Check insertion function */
			if(!ret && lun_ptr->storageaccess.usbd_msdc_init != NULL)
			{
				ret = lun_ptr->storageaccess.usbd_msdc_init(lun_ptr->storageaccess.priv);
			}

			if(!ret && lun_ptr->storageaccess.usbd_msdc_getdrvinfo != NULL)
			{
				lun_ptr->storageaccess.usbd_msdc_getdrvinfo(lun_ptr->storageaccess.priv, &inf);
				lun_ptr->secSize = (INT32U) inf.nSectors;
				lun_ptr->status = LUNSTS_NORMAL;

				//DBG_PRINT("USBDL2MSDC: LUN %d Storage init ok \r\n", i);
			}
			else
			{
				/* Storage init faile */
				lun_ptr->secSize = 0;
				lun_ptr->status = LUNSTS_NOMEDIA;
				if(lun_ptr->storageaccess.usbd_msdc_uninit != NULL)
				{
					lun_ptr->storageaccess.usbd_msdc_uninit(lun_ptr->storageaccess.priv);
				}

				DBG_PRINT("USBDL2MSDC: LUN %d Storage init failed \r\n", i);
			}
		}
	}	//for loop

	if(!msdc_ctlblk.maxlun)
	{
		return STATUS_FAIL;
	}

	return STATUS_OK;
}

INT32S drv_l2_usbd_msdc_init(void)
{
	INT32U	i;

	/* Allocate DMA buffer size */
	bulkdmabuf = (INT8U *) gp_malloc_align((dmabufsize * BOT_DMA_BUF_NUM), 16);

	if(bulkdmabuf == NULL)
	{
		DBG_PRINT("USBDL2MSDC: MSDC DMA buffer alloc failed!!\r\n");
		return STATUS_FAIL;
	}

	gp_memset((INT8S *) &msdc_ctlblk, 0, sizeof(msdc_ctlblk));

	/* Assign lun pointer to msdc_ctlblk */
	msdc_ctlblk.lunptr = g_arlun;

	/* Init LUN */
	if(msdc_ctlblk.lunptr)
	{
		drv_l2_usbd_msdc_init_lun();
	}

	/* default BOT state is CBW */
	msdc_ctlblk.prostate = BULK_CBW_STA;

	drv_l2_usbd_register_state_handler(USBD_XFER_BULK, NULL, drv_l2_usbd_msdc_state_bot_in, drv_l2_usbd_msdc_state_bot_out);

	/* init DMA buffer stucture and buffer pointer */
	for(i = 0; i < BOT_DMA_BUF_NUM; i++)
	{
		gp_memset((INT8S *) &msdc_dma_buf[i], 0, sizeof(msdc_dma_buf[i]));

		msdc_dma_buf[i].dataptr = (INT8U *) (bulkdmabuf + (dmabufsize * i));

		if(i == (BOT_DMA_BUF_NUM - 1))	//last structure, point to first
			msdc_dma_buf[i].next = (USBD_MSDC_DMA_BUF *) &msdc_dma_buf[0];
		else
			msdc_dma_buf[i].next = (USBD_MSDC_DMA_BUF *) &msdc_dma_buf[i + 1];

		msdc_dma_buf[i].usedbuffsize = 0;

		//USBD_L2MSDCLOG("USBDL2MSDC: i %d msdc_dma_buf %p, size %d!!\r\n", i,msdc_dma_buf[i].dataptr, dmabufsize);
		DBG_PRINT("USBDL2MSDC: i %d msdc_dma_buf %p, size %d!!\r\n", i, msdc_dma_buf[i].dataptr, dmabufsize);
	}

	/* allocate memory for SCSI command */
	msdc_ctlblk.dataptr = bulkpiobuf;
	if(msdc_ctlblk.dataptr == NULL)
	{
		//USBD_L2MSDCLOG("USBDL2MSDC: msdc_ctlblk.dataptr alloc failed!!\r\n");
		DBG_PRINT("USBDL2MSDC: msdc_ctlblk.dataptr alloc failed!!\r\n");
		return STATUS_FAIL;
	}

	DBG_PRINT("msdc_ctlblk.dataptr =0x%x\r\n", msdc_ctlblk.dataptr);

	/* Init SCSI inquiry data pointer, normal and CDROM */
	msdc_ctlblk.SCSIinquirydataptr = Default_scsi_inquirydata;
	msdc_ctlblk.SCSIinquiryCDdataptr = Default_scsi_inquirydata_CDROM;

	/* Init SCSI vendor command handle */
	msdc_ctlblk.process_scsi_vendor_cmd = drv_l2_usbd_scsi_vendor_cmd;

	return STATUS_OK;
}

void drv_l2_usbd_msdc_uninit(void)
{
	INT32U	i;

	for(i = 0; i < BOT_DMA_BUF_NUM; i++)
	{
		gp_memset((INT8S *) &msdc_dma_buf[i], 0, sizeof(msdc_dma_buf[i]));
	}

	/* free data pointer */
	if(msdc_ctlblk.dataptr != NULL)
	{
        gp_free(msdc_ctlblk.dataptr);
		msdc_ctlblk.dataptr = NULL;
	}

	/* Reset msdc control block */
	gp_memset((INT8S *) &msdc_ctlblk, 0, sizeof(msdc_ctlblk));

	/* Do storage uninit and reset all LUN structure */
	for(i = 0; i < MAX_MSDC_LUN_NUM; i++)
	{
		if(g_arlun[i].storageaccess.usbd_msdc_uninit != NULL)
		{
			g_arlun[i].storageaccess.usbd_msdc_uninit(g_arlun[i].storageaccess.priv);
		}

		drv_l2_usbd_msdc_reset_lun(i);
	}

	/* free BULK DMA buffer */
	if(bulkdmabuf != NULL)
	{
		gp_free(bulkdmabuf);
		bulkdmabuf = NULL;
	}

	/* un-register state handler */
	drv_l2_usbd_register_state_handler(USBD_XFER_BULK, NULL, NULL, NULL);
}

void drv_l2_usbd_msdc_reset(void)
{
	/* reset CBW, CSW */
	msdc_ctlblk.prostate = BULK_CBW_STA;
}
